#include "token.h"
#include "input.h"
#include "state.h"
#include "statemachine.h"



static bool isident(int ch)
{
    return isalnum(ch) || ch == '_';
}


bool STStart::accept(OutputStream &out, InputStream &in, StateMachine &sm, int ch) {
		if (isspace(ch)) 
			return true;
		else if (isdigit(ch)) {
			out.write(ch);
			sm.changeState(INT);
		} else if (isident(ch)) {
			out.write(ch);
			sm.changeState(IDENT);
		} else if (ch == '.') {
			out.write(ch);
			sm.changeState(FLOAT);
		} else if (ch == '"') {
            out.write(ch);
			sm.changeState(STRING);
		} else if (ch == '/') {
            if ((ch = in.getc()) == '/')
                 sm.changeState(COMMENTLINE);
            else {
                 in.ungetc(ch);
                 in.ungetc('/');
                 sm.changeState(OP);
            }    		
		} else {
			sm.changeState(OP);
			in.ungetc(ch);
		}

		return true;
}

	
	
bool STString::accept(OutputStream &out, InputStream &in, StateMachine &sm, int ch) {
		if (_end) {
		    _end = false;
		    return _end;
		}    
                   
        out.write(ch);

		if (_escape) {
			_escape = false;
		} else {
			if (ch == '"') 
				_end = true;
			else if (ch == '\\')
				_escape = true;
		}

		return true;
}


bool STInt::accept(OutputStream &out, InputStream &in, StateMachine &sm, int ch) {
		if (isdigit(ch)) {
			out.write(ch);
		} else if (ch == '.') {
			out.write(ch);
			sm.changeState(FLOAT);
		} else {
			return false;
		}

		return true;
}



bool STLineComment::accept(OutputStream &outt, InputStream &in, StateMachine &sm, int ch) {
       static bool out = false;
       
       if (out) {
           out = false;
           return out;
       }
           
       if (ch == '\n') {
           out = true;
       } else {
           outt.write(ch);
       }
       
       return true;         
}



bool STFloat::accept(OutputStream &out, InputStream &in, StateMachine &sm, int ch) {
		if (isdigit(ch)) {
			out.write(ch);
		} else 
			return false;

		return true;
}




STIdent::Keys   STIdent::_keywords[32] = {{"var", TOKEN_K_VAR},
                                {"true", TOKEN_K_TRUE},
                                {"false", TOKEN_K_FALSE},
                                {"while", TOKEN_K_WHILE},
                                {"if", TOKEN_K_IF},
                                {"else", TOKEN_K_ELSE},
                                {"for", TOKEN_K_FOR},
                                {"break", TOKEN_K_BREAK},
                                {"continue", TOKEN_K_CONTINUE},
                                {"func", TOKEN_K_FUNC},
                                {"return", TOKEN_K_RETURN},
                                {NULL, TOKEN_K_NONE},
                                };


bool STIdent::accept(OutputStream &out, InputStream &in, StateMachine &sm, int ch) {
        if (_out == NULL)
                _out = &out;
                
		if (isident(ch)) {
			out.write(ch);
		} else 
			return false;
	
		return true;
}


Token STIdent::token()  { 
		const char *p = _out->getStr();
		const Keys *k = _keywords;
     
		while (k->_keyName != NULL) {
			if (strcmp(k->_keyName, p) == 0)
			return k->_keyToken;
			++k;
		} 
     
		return TOKEN_IDENT;
}



STOp::OPAux STOp::_op[3][32] = {
	{{'#',  0,  0, TOKEN_OP_NUMBER},
     {'.',  0,  0, TOKEN_OP_DOT},
	 {',',  0,  0, TOKEN_OP_COMMA},
	 {'(',  0,  0, TOKEN_OP_LPARA},
	 {')',  0,  0, TOKEN_OP_RPARA},
	 {';',  0,  0, TOKEN_OP_SEMICOLON},
	 {'[',  0,  0, TOKEN_OP_LBRACKET},
	 {']',  0,  0, TOKEN_OP_RBRACKET},
	 {'{',  0,  0, TOKEN_OP_LBRACE},
	 {'}',  0,  0, TOKEN_OP_RBRACE},
	 {'*',  1,  0, TOKEN_AM_MUL},
	 {'+',  2,  1, TOKEN_AM_ADD},
	 {'-',  2,  3, TOKEN_AM_SUB},	
	 {'/',  1,  5, TOKEN_AM_DIV},
	 {'%',  1,  6, TOKEN_AM_MOD},
	 {'^',  1,  7, TOKEN_AM_XOR},
	 {'&',  2,  8, TOKEN_BW_AND},
	 {'|',  2, 10, TOKEN_BW_OR},
	 {'!',  1, 12, TOKEN_UO_NOT},
	 {'-',  0,  0, TOKEN_UO_SUB},
	 {'<',  2, 13, TOKEN_LG_LESS},
	 {'>',  2, 15, TOKEN_LG_GREAT},
	 {'=',  1, 17, TOKEN_AS_AS},
	 {'\0', 0,  0, TOKEN_INVALID},
	},
	{{'=',  0,  0, TOKEN_AS_MUL},
	 {'+',  0,  0, TOKEN_AM_INC},
	 {'=',  0,  0, TOKEN_AS_ADD},
	 {'-',  0,  0, TOKEN_AM_DEC},
	 {'=',  0,  0, TOKEN_AS_SUB},
	 {'=',  0,  0, TOKEN_AS_DIV},
	 {'=',  0,  0, TOKEN_AS_MOD},
	 {'=',  0,  0, TOKEN_AS_XOR},
	 {'&',  0,  0, TOKEN_LG_AND},
	 {'=',  0,  0, TOKEN_AS_AND},
	 {'|',  0,  0, TOKEN_LG_OR},
	 {'=',  0,  0, TOKEN_AS_OR},
	 {'=',  0,  0, TOKEN_LG_NOT},
	 {'<',  1,  0, TOKEN_BW_LSH},
	 {'=',  0,  0, TOKEN_LG_LESSEQ},
	 {'>',  1,  0, TOKEN_BW_RSH},
	 {'=',  0,  0, TOKEN_LG_CREATEQ},
	 {'=',  0,  0, TOKEN_LG_EQ},
	 {'\0', 0,  0, TOKEN_INVALID},
	},
	{{'=',  0,  0, TOKEN_AS_LSH},
	 {'=',  0,  0, TOKEN_AS_RSH},
	 {'\0', 0,  0, TOKEN_INVALID},
	},
};



bool STOp::accept(OutputStream &out, InputStream &in, StateMachine &sm, int ch) {
		int start, n, i;
		
		if (_index == -1) {
		    _token = TOKEN_INVALID;
			start = 0;
			n = 32;
		} else {
			start =_op[_index][_offset]._start; 
			n = _op[_index][_offset]._count;
		}

		if (n != 0) {
			for (i = 0, ++_index; i < n; ++i) {
				if (_op[_index][start + i]._op == ch) {
					_offset = start+i;
					_token = _op[_index][_offset]._token;
					return true;
				} else if (_op[_index][start+i]._op == '\0') {
				    if (_index == 0) {
				        // eat this valid token
				        sm.changeState(INVALID);
					out.write(ch);
					_index = -1;
					_offset = 0;
				        return true;
				    }    
					break;
				}				
			}
		}

		_index = -1;
		_offset = 0;
		return false;
}
    
